using MultiAgentsClient.Service.SignalRService;
using MultiAgentsClient.ChatDataStructures;
using MultiAgentsClient.Manager.DataModel;
using System.Text.RegularExpressions;
using System.Management;
using System.Text;
using System.Net;
using ApiRequestType = MultiAgentsClient.Service.SignalRService.SignalRUserService.ApiRequestType;
using static MultiAgentsClient.Service.SignalRService.SignalRClientServiceBase;
using static MultiAgentsClient.Manager.ProjectManager;
using static MultiAgentsClient.Manager.LocalDataOperator;
using static MultiAgentsClient.Manager.MessageManager;
using MultiAgentsServer.Scripts.Shared.enums;

namespace MultiAgentsClient.Manager;

public class UserInfoManager : ManagerBase
{
    private readonly SignalRUserService signalRUserService;

    private readonly SystemConfigDataModel systemConfigDataModel;
    private readonly UserDataModel userDataModel;
    private readonly LocalDataOperator localDataOperator;

    private readonly Regex antiInjectionRegex = new(@"^[0-9a-zA-Z\u4E00-\u9FA5\u3000-\u303F\uFF00-\uFFEF\\^$@._\-:+*?()[\]{} ]+$");

    #region events
    // recieve command from UI
    public record LoginCommand(string Username, string Password, bool IsRemeberMe);
    public record LogoutCommand();
    public record RegisterCommand(string Username, string Password, string InvitationCode);
    public record ChangePasswordCommand(string PreviousPassword, string NewPassword);

    // recieve command from signalR
    public record FetchUserdataResponseCommand(UserDataModel UserData);
    public record LoginResponseCommand(StatusCode Status, int UserId);
    public record RegisterResponseCommand(StatusCode Status);
    public record ChangePasswordResponseCommand(StatusCode Status);
    public record StayLoginResponseCommand(string UserName, string Password);

    // send command to UI
    public record FetchUserdataCompletedCommand(UserDataModel UserData);
    public record LoginCompletedCommand(StatusCode Status);
    public record RegisterCompletedCommand(StatusCode Status);
    public record ChangePasswordCompletedCommand(StatusCode Status);
    #endregion

    public UserInfoManager(
        SystemConfigDataModel systemConfigDataModel,
        SignalRUserService signalRUserService,
        UserDataModel userDataModel,
        LocalDataOperator localDataOperator)
    {
        this.systemConfigDataModel = systemConfigDataModel;
        this.signalRUserService = signalRUserService;
        this.userDataModel = userDataModel;
        this.localDataOperator = localDataOperator;

        eventBus.Subscribe<LogoutCommand>(cmd => { Task.Run(() => LogoutCommandHandler()); });
        eventBus.Subscribe<LoginCommand>(cmd => { Task.Run(() => LoginCommandHandler(cmd)); });
        eventBus.Subscribe<RegisterCommand>(cmd => { Task.Run(() => RegisterCommandHandler(cmd)); });
        eventBus.Subscribe<ChangePasswordCommand>(cmd => { Task.Run(() => ChangePasswordCommandHandler(cmd)); });

        eventBus.Subscribe<FetchUserdataResponseCommand>(cmd => { Task.Run(() => FetchUserdataResponseCommandHandler(cmd)); });
        eventBus.Subscribe<LoginResponseCommand>(cmd => { Task.Run(() => LoginResponseCommandHandler(cmd)); });
        eventBus.Subscribe<RegisterResponseCommand>(cmd => { Task.Run(() => RegisterResponseCommandHandler(cmd)); });
        eventBus.Subscribe<ChangePasswordResponseCommand>(cmd => { Task.Run(() => ChangePasswordResponseCommandHandler(cmd)); });
        eventBus.Subscribe<StayLoginResponseCommand>(cmd => { Task.Run(() => StayLoginResponseCommandHandler(cmd)); });
        eventBus.Subscribe<ConnectServerCommand>(ConnectServer);
    }

    #region request
    private async Task LoginCommandHandler(LoginCommand loginCmd)
    {
        if (antiInjectionRegex.IsMatch(loginCmd.Username) == false ||
            antiInjectionRegex.IsMatch(loginCmd.Password) == false)
        {
            return;
        }

        string hostName = Dns.GetHostName();

        string macAddress = "";
        string motherboardSerialNumber = "";

#if WINDOWS
        try
        {
            string searchString = "select * from Win32_NetworkAdapter WHERE NetConnectionID IS NOT NULL";
            ManagementObjectSearcher searcher = new(searchString);

            foreach (ManagementObject obj in searcher.Get().Cast<ManagementObject>())
            {
                if (obj.Properties["MACAddress"] != null)
                {
                    macAddress = obj["MACAddress"].ToString() ?? throw new Exception("Can not find MAC address.");
                    break;
                }
            }

            searchString = "SELECT Win32_BaseBoard FROM SerialNumber";
            searcher = new ManagementObjectSearcher(searchString);
            foreach (ManagementObject obj in searcher.Get())
            {
                motherboardSerialNumber = obj["Win32_BaseBoard"].ToString() ?? throw new Exception("Can not find motherboard serial numbers.");
                break;
            }
        }
        catch { }
#endif

        string combinedString = hostName + macAddress + motherboardSerialNumber + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        byte[] bytes = System.Security.Cryptography.SHA256.HashData(Encoding.UTF8.GetBytes(combinedString));
        string sessionId = Convert.ToBase64String(bytes);

        systemConfigDataModel.sessionId = sessionId;

        localDataOperator.ClearLoginInformation();
        await signalRUserService.SendSignalRRequest(ApiRequestType.LoginApiRequest.ToString(), new Dictionary<string, object>()
        {
            { "userName", loginCmd.Username },
            { "userPassword", loginCmd.Password },
            { "isRememberMe", loginCmd.IsRemeberMe },
            { "sessionId", sessionId },
            { "motherboardSerialNumber", motherboardSerialNumber },
        });
    }

    private async Task LogoutCommandHandler()
    {
        localDataOperator.ClearLoginInformation();
        await signalRUserService.SendSignalRRequest(ApiRequestType.LogoutApiRequest.ToString(), new Dictionary<string, object>()
        {
            { "sessionId", systemConfigDataModel.sessionId },
            { "userId", userDataModel.Id },
        });
    }

    private async Task RegisterCommandHandler(RegisterCommand registerCmd)
    {
        if (antiInjectionRegex.IsMatch(registerCmd.Username) == false ||
            antiInjectionRegex.IsMatch(registerCmd.Password) == false)
        {
            return;
        }

        await signalRUserService.SendSignalRRequest(ApiRequestType.RegisterApiRequest.ToString(), new Dictionary<string, object>()
        {
            { "userName", registerCmd.Username },
            { "userPassword", registerCmd.Password },
            { "invitationCode", registerCmd.InvitationCode },
        });
    }

    private async Task ChangePasswordCommandHandler(ChangePasswordCommand cmd)
    {
        if (antiInjectionRegex.IsMatch(cmd.PreviousPassword) == false ||
            antiInjectionRegex.IsMatch(cmd.NewPassword) == false)
        {
            return;
        }

        await signalRUserService.SendSignalRRequest(ApiRequestType.ChangePasswordApiRequest.ToString(), new Dictionary<string, object>()
        {
            { "sessionId", systemConfigDataModel.sessionId },
            { "userId", userDataModel.Id },
            { "previousPassword", cmd.PreviousPassword },
            { "newPassword", cmd.NewPassword },
        });
    }
    #endregion

    #region response
    private async void FetchUserdataResponseCommandHandler(FetchUserdataResponseCommand cmd)
    {
        await NavigationManager.IsCurrentPage(typeof(MainPage));
        userDataModel.DeepCopy(cmd.UserData);
        eventBus.Publish(new FetchUserdataCompletedCommand(userDataModel));
        ChatProject? selectedProject = cmd.UserData.chatProjects.FirstOrDefault(c => c.Id == cmd.UserData.selectedPrjId);

        List<ChatMessageData> messages = [];
        if (selectedProject != null)
        {
            ChatRoom? selectedChatRoom = selectedProject.ChatRoomList.FirstOrDefault(c => c.Id == cmd.UserData.selectedChatRoomId);
            if (selectedChatRoom != null)
            {
                messages = selectedChatRoom.defaultAgent.ChatListContainer.ChatList ?? [];
            }
        }

        if (cmd.UserData.selectedChatRoomId > 0)
        {
            await Task.Delay(1000); // TODO: remove (to wait message webview load)
            eventBus.Publish(new SwitchChatRoomCommand(cmd.UserData.selectedChatRoomId));
        }

        if (Convert.ToBoolean(localDataOperator.GetUserSettings(BasicUserSettings.IsRememberRoomModel)))
        {
            string model = Convert.ToString(localDataOperator.GetUserSettingsData(BasicUserSettingsData.RoomModel));

            systemConfigDataModel.selectingModel = model;

            eventBus.Publish(new LLMModelSelectionChangedComleteCommand(model));
        }
        else
        {
            eventBus.Publish(new LLMModelSelectionChangedComleteCommand(""));
        }
    }

    private async void LoginResponseCommandHandler(LoginResponseCommand cmd)
    {
        await NavigationManager.IsCurrentPage(typeof(LoginPage));

        if (cmd.Status == StatusCode.Status200)
        {
            _ = signalRUserService.SendSignalRRequest(ApiRequestType.FetchUserdataApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", cmd.UserId },
            });
        }

        eventBus.Publish(new LoginCompletedCommand(cmd.Status));
        eventBus.Publish(new SessionStartCommand(systemConfigDataModel.sessionId, cmd.UserId));
    }

    private void RegisterResponseCommandHandler(RegisterResponseCommand loginCmd)
    {
        eventBus.Publish(new RegisterCompletedCommand(loginCmd.Status));
    }

    private void ChangePasswordResponseCommandHandler(ChangePasswordResponseCommand cmd)
    {
        eventBus.Publish(new ChangePasswordCompletedCommand(cmd.Status));
    }

    private void StayLoginResponseCommandHandler(StayLoginResponseCommand cmd)
    {
        localDataOperator.SaveLoginInformation(cmd.UserName, cmd.Password, systemConfigDataModel.selectingServerName, systemConfigDataModel.sessionId);
    }

    private void ConnectServer(ConnectServerCommand cmd)
    {
        systemConfigDataModel.selectingServerName = cmd.ServerName;
    }
    #endregion
}
